De momento solo hemos hecho validaciones básicas usando la anotación @Required de OpenXava. A veces es necesario escribir nuestra propia lógica de validación. En el apéndice C se explica como funciona la validación. Aquí vamos a añadir validaciones con lógica propia a tu aplicación.
Alternativas de validación
Vamos a refinar tu código para que el usuario no pueda asignar pedidos a una factura si los pedidos no han sido entregados todavía. Es decir, solo los pedidos entregados pueden asociarse a una factura. Aprovecharemos la oportunidad para explorar diferentes formas de hacer esta validación.
Añadir la propiedad entregado a Pedido
Para hacer esto, lo primero es añadir una nueva propiedad a la entidad Pedido. La propiedad entregado:
Ahora tienes una nueva propiedad entregado que el usuario puede marcar para indicar que el pedido ha sido entregado. Ejecuta el nuevo código y marca algunos de los pedidos existentes como entregados.
Nota:
Al arrancar la aplicación se añadirá la columna entregado a la tabla DocumentoComercial, ésta tendrá el valor null para todos los registros. Debemos actualizar esta columna con las siguiente sentencia SQL:
UPDATE DocumentoComercial
SET entregado =false
Validar con @EntityValidator
En tu aplicación actual el usuario puede añadir cualquier pedido que le plazca a una factura usando el módulo Factura, y puede asignar una factura a cualquier pedido desde el módulo Pedido. Vamos a restringir esto. Solo los pedidos entregados podrán añadirse a una factura.
La primera alternativa que usaremos para implementar esta validación es mediante @EntityValidator. Esta anotación te permite asignar a tu entidad una clase con la lógica de validación deseada. Anotemos tu entidad Pedido tal como muestra el siguiente código:
@EntityValidator(
value=ValidadorEntregadoParaEstarEnFactura.class, // Clase con la lógica de validación
properties= {
@PropertyValue(name="anyo"), // El contenido de estas propiedades
@PropertyValue(name="numero"), // se mueve desde la entidad 'Pedido'
@PropertyValue(name="factura"), // al validador antes de
@PropertyValue(name="entregado")// ejecutar la validación})publicclass Pedido extends DocumentoComercial {
Cada vez que un objeto Pedido se crea o modifica un objeto del tipo ValidadorEntregadoParaEstarEnFactura es creado, entonces las propiedades anyo, numero, factura y entregado se rellenan con las propiedades del mismo nombre del objeto Pedido. Después de eso, el método validate() del validador se ejecuta. Puedes ver el código del validador:
packageorg.openxava.facturacion.validadores;// En el paquete 'validadores'importorg.openxava.facturacion.modelo.*;importorg.openxava.util.*;importorg.openxava.validators.*;publicclass ValidadorEntregadoParaEstarEnFactura
implements IValidator {// ha de implementar 'IValidator'privateint anyo;// Propiedades a ser inyectadas desde Pedidoprivateint numero;privateboolean entregado;private Factura factura;
@Overridepublicvoid validate(Messages errors)throwsException{// La lógica de validaciónif(factura == null)return;if(!entregado){
errors.add(// Al añadir mensajes a 'errors' la validación fallará"pedido_debe_estar_entregado", // Un id del archivo i18n
anyo, numero);// Argumentos para el mensaje}}// Getters y setters para anyo, numero, entregado y factura
...
}
La lógica de validación es extremadamente fácil, si una factura está presente y este pedido no ha sido servido añadimos un mensaje de error, por tanto la validación fallará. Has de añadir el mensaje de error en el archivo Facturacion/i18n/MensajesFacturacion_es.properties. Tal como muestra a continuación:
# Mensajes para la aplicación Facturacionpedido_debe_estar_entregado=Pedido {0}/{1} debe estar entregado para ser añadido a una Factura
Ahora puedes intentar añadir pedidos a una factura con la aplicación, verás como los pedidos no servidos son rechazados. Como se ve en la siguiente figura:
Ya tienes tu validación hecha con @EntityValidator. No es difícil, pero es un poco verboso, porque necesitas escribir una clase nueva solo para añadir 2 línea de lógica. Aprendamos otras formas de hacer esta misma validación.
Validar con métodos de retrollamada JPA
Vamos a probar otra forma más sencilla de hacer esta validación, simplemente moviendo la lógica de validación desde la clase validador a la misma entidad Pedido, en este caso a un método @PreUpdate.
Lo primero es eliminar la clase ValidadorEntregadoParaEstarEnFactura de tu proyecto. También quita la anotación @EntityValidator de tu entidad Pedido:
Acabamos del eliminar la validación. Ahora, vamos a añadirla de nuevo, pero ahora dentro de la misma clase Pedido. Escribe el método validar() que se muestra a continuación dentro de tu clase Pedido:
@PreUpdate
privatevoid validar()throwsException{if(factura != null&&!isEntregado()){// La lógica de validación// La excepción de validación del entorno Bean Validationthrownew javax.validation.ValidationException(
XavaResources.getString(// Para leer un mensaje i18n"pedido_debe_estar_entregado",
getAnyo(),
getNumero()));}}
Antes de grabar un pedido esta validación se ejecutará, si falla una ValidationException será lanzada. Esta excepción es del marco de validación Bean Validation, de esta forma OpenXava sabe que es una excepción de validación. Así con solo un método dentro de tu entidad tienes la validación hecha.
Validar en el setter
Otra alternativa para hacer tu validación es poner tu lógica de validación dentro del método setter. Es un enfoque simple y llano. Para probarlo, quita el método validar() de tu entidad Pedido y modifica el método setFactura() de la forma que mostramos a continuación:
publicvoid setFactura(Factura factura){if(factura != null&&!isEntregado()){// La lógica de validación// La excepción de validación del entorno Bean Validationthrownew javax.validation.ValidationException(
XavaResources.getString(// Para leer un mensaje i18n"pedido_debe_estar_entregado",
getAnyo(),
getNumero()));}this.factura = factura;// La asignación típica del setter}
Esto funciona exactamente como las dos opciones anteriores. Es parecida a la alternativa del @PreUpdate, solo que no depende de JPA, es una implementación básica de Java.
Validar con Bean Validation
Como opción final vamos a hacer la más breve. Consiste en poner tu lógica de validación dentro de un método booleano anotado con la anotación de Bean Validation @AssertTrue.
Para implementar esta alternativa primero quita la lógica de validación del método setFactura(). Después, añade isEntregadoParaEstarEnFactura() a tu entidad Pedido, como se muestra a continuación:
@AssertTrue(// Antes de grabar confirma que el método devuelve true, si no lanza una excepción
message="pedido_debe_estar_entregado"// Mensaje de error en caso retorne false)privateboolean isEntregadoParaEstarEnFactura(){// ...return factura == null || isEntregado();// La lógica de validación}
En las formas anteriores de validación nuestro mensaje de error era construído mediante dos argumentos, anyo y numero, que en nuestro archivo i18n son representados por {0}/{1} respectivamente. Para el caso de validación con @AssertTrue no podemos pasar estos dos argumentos para construir nuestro mensaje de error, sino que podemos declarar propiedades y propiedades calificadas del bean validado en la definición del mensaje. Veamos como modificar la definición del mensaje:
# Mensajes para la aplicación Facturacionpedido_debe_estar_entregado=Pedido {anyo}/{numero} debe estar entregado para ser añadido a una Factura
OpenXava llenará {anyo}/{numero} con los valores de anyo y numero que tenga el Pedido que está siendo actualizado y no cumple la condición de validación.
Esta es la forma más simple de validar, porque solo anotamos el método con la validación, y es el entorno Bean Validation el responsable de llamar este método al grabar, y lanzar la excepción correspondiente si la validación no pasa.
Validar al borrar con @RemoveValidator
Las validaciones que hemos visto hasta ahora se hacen cuando la entidad se modifica, pero a veces es útil hacer la validación justo al borrar la entidad, y usar la validación para vetar el borrado de la misma.
Vamos a modificar la aplicación para impedir que un usuario borre un pedido si éste tiene una factura asociada. Para hacer esto anota tu entidad Pedido con @RemoveValidator, como se muestra a continuación:
@RemoveValidator(ValidadorBorrarPedido.class)// La clase con la validaciónpublicclass Pedido extends DocumentoComercial {
Ahora, antes de borrar un pedido la lógica de ValidadorBorrarPedido se ejecuta, y si la validación falla el pedido no se borra. Veamos el código de este validador:
packageorg.openxava.facturacion.validadores;// En el paquete 'validadores'importorg.openxava.facturacion.modelo.*;importorg.openxava.util.*;importorg.openxava.validators.*;publicclass ValidadorBorrarPedido
implements IRemoveValidator {// Ha de implementar 'IRemoveValidator'private Pedido pedido;
@Overridepublicvoid setEntity(Object entity)// La entidad a borrar se inyectará...throwsException// ...con este método antes de la validación{this.pedido = (Pedido) entity;}
@Overridepublicvoid validate(Messages errors)// La lógica de validaciónthrowsException{if(pedido.getFactura()!= null){// Añadiendo mensajes a 'errors' la validación fallará y el// borrado se abortará
errors.add("no_puede_borrar_pedido_con_factura");}}}
La lógica de validación está en el método validate(). Antes de llamarlo la entidad a validar es inyectada usando setEntity(). Si se añaden mensajes al objeto errors la validación fallará y la entidad no se borrará. Has de añadir el mensaje de error en el archivo Facturacion/i18n/MensajesFacturacion_es.properties:
no_puede_borrar_pedido_con_factura=Pedido asociado a factura no puede ser eliminado
Ahora si intentas borrar un pedido con una factura asociada obtendrás un mensaje de error y el borrado no se producirá.
Puedes ver que usar un @RemoveValidator no es difícil, pero es un poco verboso. Has de escribir una clase nueva solo para añadir un simple if. Examinemos una alternativa más breve:
Validar al borrar con un método de retrollamada
Vamos a probar otra forma más simple de hacer esta validación al borrar, moviendo la lógica de validación desde la clase validador a la misma entidad Pedido, en este caso en un método @PreRemove.
El primer paso es eliminar la clase ValidadorBorrarPedido de tu proyecto. Además quita la anotación @RemoveValidator de tu entidad Pedido:
Hemos quitado la validación. Añadámosla otra vez, pero ahora dentro de la misma clase Pedido. Añade el método validarPreBorrar() a la clase Pedido, como se muestra a continuación:
@PreRemove
privatevoid validarPreBorrar(){if(factura != null){// La lógica de validaciónthrownew javax.validation.ValidationException(// Lanza una excepción runtime
XavaResources.getString(// Para obtener un mensaje de texto"no_puede_borrar_pedido_con_factura"));}}
Antes de borrar un pedido esta validación se efectuará, si falla se lanzará una ValidationException. Puedes lanzar cualquier excepción runtime para abortar el borrado. Tan solo con un método dentro de la entidad tienes la validación hecha.
¿Cuál es la mejor forma de validar?
Has aprendido varias formas de hacer la validación sobre tus clases del modelo. ¿Cuál de ellas es la mejor? Todas ellas son opciones válidas. Depende de tus circunstancias y preferencias personales. Si tienes una validación que no es trivial y es reutilizable en varios puntos de tu aplicación, entonces usar un @EntityValidator y @RemoveValidator es una buena opción. Por otra parte, si quieres usar tu modelo fuera de OpenXava y sin JPA, entonces el uso de la validación en los setters es mejor.
En nuestro caso particular hemos optado por @AssertTrue para la validación “el pedido ha de estar servido para estar en una factura” y por @PreRemove para la validación al borrar. Ya que son las alternativas más simples que funcionan.
Crear tu propia anotación de Bean Validation
Las técnicas mencionadas hasta ahora son muy útiles para la mayoría de las validaciones de tus aplicaciones. Sin embargo, a veces te encuentras con algunas validaciones que son muy genéricas y quieres usarlas una y otra vez. En este caso definir tu propia anotación de Bean Validation puede ser una buena opción. Definir un Bean validation es más largo y engorroso que lo que hemos visto hasta ahora, pero usarlo y reusarlo es simple, tan solo añadir una anotación a tu propiedad o clase.
Vamos a aprender como crear un Bean Validation.
Usar un Bean Validation en tu entidad
Es superfácil. Simplemente anota tu propiedad como ves a a continuación:
@ISBN // Esta anotación indica que esta propiedad tiene que validarse como un ISBNprivateString isbn;
Solo con añadir @ISBN a tu propiedad, y ésta será validada justo antes de que la entidad se grabe en la base de datos, ¡genial! El problema es que @ISBN no está incluida como un validador predefinido en el marco de validación Bean Validation. Esto no es un gran problema, si quieres una anotación @ISBN, hazla tú mismo. De hecho, vamos a crear la anotación de validación @ISBN en esta sección.
Antes de nada, añadamos una nueva propiedad isbn a Producto. Edita tu clase Producto y añádele el siguiente código:
Ejecuta el módulo Producto con tu navegador. Sí, la propiedad isbn ya está ahí. Ahora, puedes añadir la validación.
Definir tu propia anotación ISBN
Creemos la anotación @ISBN. Primero, crea un paquete en tu proyecto llamado org.openxava.facturacion.anotaciones, entonces sigue las instrucciones de la siguiente figura para crear una nueva anotación llamada ISBN.
Edita el código de tu recién creada anotación ISBN y déjala así:
packageorg.openxava.facturacion.anotaciones;// En el paquete 'anotaciones'importjava.lang.annotation.*;importjavax.validation.*;importorg.openxava.facturacion.validadores.*;
@Constraint(validatedBy = ValidadorISBN.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)public @interface ISBN {Class<?>[] groups()default{};Class<?extends Payload>[] payload()default{};String message()default"No existe ISBN";// Definimos un mensaje por defecto}
Como puedes ver, es una definición de anotación normal y corriente, solo que usas @Constraint para indicar la clase con la lógica de validación. Escribamos la clase ValidadorISBN.
Usa Apache Commons Validator para implementar la lógica
Vamos a escribir la clase ValidadorISBN con la lógica de validación para un ISBN. En lugar de escribir nosotros mismos la lógica para validar un ISBN usaremos el proyecto Commons Validator de Apache. Commons Validator contiene algoritmos de validación para direcciones de correo electrónico, fechas, URL y así por el estilo. El commons-validator.jar se incluye por defecto en los proyectos OpenXava, por tanto lo puedes usar sin ninguna configuración adicional.
El código para ValidadorISBN lo puedes ver a continuación:
packageorg.openxava.facturacion.validadores;// En el paquete 'validadores'importjavax.validation.*;importorg.openxava.facturacion.anotaciones.*;importorg.openxava.util.*;publicclass ValidadorISBN implements ConstraintValidator<ISBN, Object>{privatestatic org.apache.commons.validator.routines.ISBNValidator
validador = // De 'Commons Validator'new org.apache.commons.validator.routines.ISBNValidator();
@Overridepublicvoid initialize(ISBN isbn){}
@Overridepublicboolean isValid(Object value,
ConstraintValidatorContext contexto){// Contiene la lógica de validaciónif(Is.empty(value))returntrue;return validador.isValid(value.toString());// Usa 'Commons Validator'}}
Como ves, la clase validador tiene que implementar ConstraintValidator del paquete javax.validation. Esto fuerza a tu validador a implementar initialize() e isValid(). El método isValid() contiene la lógica de validación. Fíjate que si el elemento a validar está vacío asumimos que es válido, porque validar si un valor está presente es responsabilidad de otras anotaciones, como @Required, y no de @ISBN.
En este caso la lógica de validación es sencillísima, porque nos limitamos a llamar al validador ISBN de Apache Commons Validator. @ISBN está listo para usar. Para hacerlo anota tu propiedad isbn con él. Puedes ver cómo:
Ahora, puedes probar tu módulo, y verificar que el ISBN que introduces se valida correctamente. Enhorabuena, has escrito tu primer Bean Validation. No ha sido tan difícil: una anotación, una clase.
Este @ISBN es suficientemente bueno para usarlo en la vida real, sin embargo, vamos a mejorarlo un poco más, y así tendremos la posibilidad de experimentar con algunas posibilidades interesantes.
Llamar a un servicio web REST para validar el ISBN
Aunque la mayoría de los validadores tienen una lógica simple, puedes crear validadores con una lógica compleja si lo necesitas. Por ejemplo, en el caso de nuestro ISBN, queremos, no solo verificar el formato correcto, sino también comprobar que existe de verdad un libro con ese ISBN. Una forma de hacer esto es usando servicios web.
Como seguramente ya sepas, un servicio web es una funcionalidad que reside en un servidor web y que tú puedes llamar desde tu programa. La forma tradicional de desarrollar servicios web es mediante los estándares WS-*, como SOAP, UDDI, etc. Aunque, hoy en día, la forma más simple de desarrollar servicios es REST. REST consiste básicamente en usar la ya existente “forma de trabajar” de internet para comunicación entre programas. Llamar a un servicio REST consiste en usar una URL web convencional para obtener un recurso de un servidor web. Este recurso usualmente contiene datos en formato XML, HTML, JSON, etc. En otras palabras, los programas usan internet de la misma manera que lo hacen los usuarios con sus navegadores.
Hay bastantes sitio con servicios web SOAP y REST para consultar el ISBN de un libro, pero no suele ser gratis. Por tanto, vamos a usar una alternativa más barata, que va a ser llamar a un sitio web convencional para hacer la búsqueda del ISBN, y examinar después la página resultado para determinar si la búsqueda ha funcionado. Algo así como un servicio web pseudo-REST.
Para llamar a la página web usaremos el marco de trabajo HtmlUnit. Aunque el principal cometido de este marco de trabajo sea crear pruebas para tus aplicaciones web, puedes usarlo para leer cualquier página web. Lo usaremos porque es más fácil que otras librerías con este propósito, como por ejemplo Apache Commons HttpClient. Observa lo simple que es leer una página web con HtmlUnit:
WebClient cliente = new WebClient();
HtmlPage pagina = (HtmlPage) cliente.getPage("http://www.openxava.org/");
Después de esto, puedes usar el objeto pagina para manipular la página leída.
OpenXava usa HtmlUnit como marco subyacente para las pruebas, por tanto ya está incluido en OpenXava, pero no se incluye por defecto en las aplicaciones OpenXava, así que tienes que incluirlo tú mismo en tu aplicación. Para hacerlo copia los archivos cssparser.jar, htmlunit-core-js.jar, htmlunit.jar, httpclient.jar, httpcore.jar, httpmime.jar, nekothml.jar, sac.jar, xalan.jar, xercesImpl.jar, xml-apis.jar, desde la carpeta Openxava/lib a la carpeta Facturacion/web/WEB-INF/lib.
Modifiquemos ValidadorISBN para que haga uso de este servicio REST. Puedes ver el resultado:
packageorg.openxava.facturacion.validadores;// En el paquete 'validadores'importjavax.validation.*;importorg.apache.commons.logging.*;// Para usar 'Log'importorg.openxava.facturacion.anotaciones.*;importorg.openxava.util.*;importcom.gargoylesoftware.htmlunit.*;// Para usar 'HtmlUnit'importcom.gargoylesoftware.htmlunit.html.*;publicclass ValidadorISBN implements ConstraintValidator<ISBN, Object>{privatestatic Log log = LogFactory.getLog(ValidadorISBN.class);// Instanciamos un 'Log'privatestatic org.apache.commons.validator.routines.ISBNValidator
validador = new org.apache.commons.validator.routines.ISBNValidator();
@Overridepublicvoid initialize(ISBN isbn){}
@Overridepublicboolean isValid(Object value,
ConstraintValidatorContext contexto){// Contiene la lógica de validaciónif(Is.empty(value))returntrue;if(!validador.isValid(value.toString()))returnfalse;return existeIsbn(value);// Aquí hacemos la llamada REST}privateboolean existeIsbn(Object isbn){String mensajeError = "can't be found";// Mensaje de búsqueda fallida en 'bookfinder4u'try{
WebClient cliente = new WebClient();
HtmlPage pagina = (HtmlPage) cliente.getPage(// Llamamos a"http://www.bookfinder4u.com/" + // bookfinder4u"IsbnSearch.aspx?isbn=" + // con una url para buscar
isbn + "&mode=direct");// por ISBNreturn pagina.asText()// Comprueba si la página resultante
.indexOf(mensajeError)<0;// no tiene el mensaje de error}catch(Exception ex){
log.warn("Imposible conectar a bookfinder4u " +
"para validar ISBN. Validación falló", ex);returnfalse;// Si hay algún error asumimos que la validación falló}}}
Simplemente buscamos una página usando como argumento en la URL el ISBN. Si la página resultante no contiene el mensaje de error "can't be found" la búsqueda ha sido satisfactoria. El método pagina.asText() devuelve el contenido de la página HTML sin las marcas HTML, es decir, con solo la información textual.
Puedes usar este truco con cualquier sitio que te permita hacer búsquedas, así puedes consultar virtualmente millones de sitios web desde tu aplicación. En un servicio REST más puro el resultado hubiera sido un documento JSON o XML en vez de uno HTML, pero hubieras tenido que pasar por caja.
Prueba ahora tu aplicación y verás como si introduces un ISBN no existente la validación falla.
Añadir atributos a tu anotación
Creas una anotación Bean Validation cuando quieres reutilizar la validación varias veces, usualmente en varios proyectos. En este caso, necesitas hacer tu validación adaptable, para que sea reutilizable de verdad. Por ejemplo, en el proyecto actual buscar en www.bookfinder4u.com el ISBN es conveniente, pero en otro proyecto, o incluso en otra entidad de tu actual proyecto, puede que no quieras hacer esa búsqueda. Necesitas hacer tu anotación más flexible.
La forma de añadir esta flexibilidad a tu anotación de validación es mediante los atributos. Por ejemplo, podemos añadir un atributo de búsqueda booleano a nuestra anotación ISBN para poder escoger si queremos buscar el ISBN en internet para validar o no. Para hacerlo, simplemente añade el atributo buscar al código de la anotación ISBN, tal como muestra el siguiente código:
public @interface ISBN {boolean buscar()defaulttrue;// Para (des)activar la búsqueda web al validar// ... el resto del código}
Este nuevo atributo buscar puede leerse de la clase validador. Míra como:
publicclass ValidadorISBN implements ConstraintValidator<ISBN, Object>{// ...privateboolean buscar;// Almacena la opción buscar
@Overridepublicvoid initialize(ISBN isbn){// Lee los atributos de la anotaciónthis.buscar = isbn.buscar();}
@Overridepublicboolean isValid(Object value, ConstraintValidatorContext contexto){if(Is.empty(value))returntrue;if(!validador.isValid(value.toString()))returnfalse;return buscar ? existeIsbn(value) : true;// Usa 'buscar'}// ...}
Aquí ves la utilidad del método initialize(), que lee la anotación para inicializar el validador. En este caso simplemente almacenamos el valor de isbn.buscar() para preguntar por él en isValid().
Ahora puedes escoger si quieres llamar a nuestro servicio pseudo-REST o no para hacer la validación ISBN. Mira como:
@ISBN(buscar=false)// En este caso no se hace una búsqueda en la web para validar el ISBNprivateString isbn;
¡Enhorabuena! Has aprendido como crear tu propia anotación de Bean Validation, y de paso a usar la útil herramienta HtmlUnit.
Pruebas JUnit
Nuestra meta no es desarrollar una ingente cantidad de código, sino crear software de calidad. Al final, si creas software de calidad acabarás escribiendo más cantidad de software, porque podrás dedicar más tiempo a hacer cosas nuevas y excitantes, y menos depurando legiones de bugs. Y tú sabes que la única forma de conseguir calidad es mediante las pruebas automáticas, por tanto actualicemos nuestro código de prueba.
Probar la validación al añadir a una colección
Recuerda que hemos refinado tu código para que el usuario no pueda asignar pedidos a una factura si los pedidos no están servidos. Después de esto, tu actual testAnyadirPedidos() de PruebaFactura puede fallar, porque trata de añadir el primer pedido, y es posible que ese primer pedido no esté marcado como servido.
Modifiquemos la prueba para que funcione y también para comprobar la nueva funcionalidad de validación. Mira como:
publicvoid testAnyadirPedidos()throwsException{
login("admin", "admin");
assertListNotEmpty();
execute("List.orderBy", "property=numero");
execute("Mode.detailAndFirst");
execute("Sections.change", "activeSection=1");
assertCollectionRowCount("pedidos", 0);
execute("Collection.add",
"viewObject=xava_view_section1_pedidos");// execute("AddToCollection.add", "row=0"); // Ahora no seleccionamos al azar
seleccionarPrimerPedidoConEntregadoIgual("Sí");// Selecciona un pedido entregado
seleccionarPrimerPedidoConEntregadoIgual("No");// Selecciona uno no entregado
execute("AddToCollection.add");// Tratamos de añadir ambos
assertError(// Un error, porque el pedido no entregado no se puede añadir"¡ERROR! 1 elemento(s) NO añadido(s) a Pedidos de Factura");
assertMessage(// Un mensaje de confirmación, porque el pedido entregado ha sido añadido"1 elemento(s) añadido(s) a Pedidos de Factura");
assertCollectionRowCount("pedidos", 1);
checkRowCollection("pedidos", 0);
execute("Collection.removeSelected",
"viewObject=xava_view_section1_pedidos");
assertCollectionRowCount("pedidos", 0);}
Hemos modificado la parte de la selección de pedidos a añadir, antes seleccionábamos el primero, no importaba si estaba servido o no. Ahora seleccionamos un pedido servido y otro no servido, de esta forma comprobamos que el pedido servido se añade y el no servido es rechazado.
La pieza que nos falta es la forma de seleccionar los pedidos. Esto es el trabajo del método seleccionarPrimerPedidoConEntregadoIgual(). Veámoslo:
privatevoid seleccionarPrimerPedidoConEntregadoIgual(String value)throwsException{int c = getListRowCount();// El total de filas visualizadas en la listafor(int i = 0; i < c; i++){if(value.equals(
getValueInList(i, 12)))// Obtenermos valor de la columna 'entregado'{
checkRow(i);return;}}
fail("Debe tener al menos una fila con entregado=" + value);}
Aquí ves una buena técnica para hacer un bucle sobre los elementos visualizados de una lista para seleccionarlos y coger algunos datos, o cualquier otra cosa que quieras hacer con los datos de la lista.
Probar validación al asignar una referencia y al borrar
Desde el módulo Factura el usuario no pueda asignar pedidos a una factura si los pedidos no están servidos, por lo tanto, desde el módulo Pedido el usuario tampoco debe poder asignar una factura a un pedido si éste no está servido. Es decir, hemos de probar también la otra parte de la asociación. Lo haremos modificando el actual testPonerFactura() de PruebaPedido.
Además, aprovecharemos este caso para probar la validación al borrar que vimos en las secciones Validar al borrar con @RemoveValidator y Validar al borrar con un método de retrollamada. Allí modificamos la aplicación para impedir que un usuario borrara un pedido si éste tenía una factura asociada. Ahora probaremos este hecho.
Todo esto está en el testPonerFactura() mejorado que puedes ver a continuación:
publicvoid testPonerFactura()throwsException{
login("admin", "admin");
assertListNotEmpty();
execute("List.orderBy", "property=numero");// Establece el orden de la lista
execute("Mode.detailAndFirst");
assertValue("entregado", "false");// El pedido no debe estar entregado
execute("Sections.change", "activeSection=1");
assertValue("factura.numero", "");
assertValue("factura.anyo", "");
execute("Reference.search",
"keyProperty=factura.anyo");String anyo = getValueInList(0, "anyo");String numero = getValueInList(0, "numero");
execute("ReferenceSearch.choose", "row=0");
assertValue("factura.anyo", anyo);
assertValue("factura.numero", numero);// Los pedidos no entregados no pueden tener factura
execute("CRUD.save");
assertErrorsCount(1);// No podemos grabar porque no ha sido entregado
setValue("entregado", "true");
execute("CRUD.save");// Con 'entregado=true' podemos grabar el pedido
assertNoErrors();// Un pedido con factura no se puede borrar
execute("Mode.list");// Vamos al modo lista
execute("CRUD.deleteRow", "row=0");// y eliminanos el pedido grabado
assertError("Imposible borrar Pedido por: " + // No podemos borrar porque tiene"Pedido asociado a factura no puede ser eliminado");// una factura asociada// Restaurar los valores originales
execute("Mode.detailAndFirst");
setValue("entregado", "false");
setValue("factura.anyo", "");
execute("CRUD.save");
assertNoErrors();}
La prueba original solo buscaba una factura, ni siquiera intentaba grabarla. Ahora, hemos añadido código al final para probar la grabación de un pedido marcado como servido, y marcado como no servido, de esta forma comprobamos la validación. Después de eso, tratamos de borrar el pedido, el cual tiene una factura, así probamos también la validación al borrar.
Probar el Bean Validation propio
Solo nos queda probar tu Bean Validation ISBN, el cual usa un servicio REST para hacer la validación. Simplemente hemos de escribir una prueba que trate de asignar un ISBN incorrecto, uno inexistente y uno correcto a un producto, y ver que pasa. Para hacer esto añadamos un método testValidarISBN() a PruebaProducto.
publicvoid testValidarISBN()throwsException{
login("admin", "admin");// Buscar producto1
execute("CRUD.new");
setValue("numero", Integer.toString(producto1.getNumero()));
execute("CRUD.refresh");
assertValue("descripcion", "Producto JUNIT 1");
assertValue("isbn", "");// Con un formato de ISBN incorrecto
setValue("isbn", "1111");
execute("CRUD.save");// Falla por el formato (apache commons validator)
assertError("1111 no es un valor válido para ISBN de Producto: " +
"No existe ISBN");// ISBN no existe aunque tiene un formato correcto
setValue("isbn", "9791034369997");
execute("CRUD.save");// Falla porque no existe ISBN (el servicio REST)
assertError("9791034369997 no es un valor válido para ISBN de " +
"Producto: No existe ISBN");// ISBN existe
setValue("isbn", "9780932633439");
execute("CRUD.save");// No falla
assertNoErrors();}
Seguramente la prueba manual que hacías mientras estabas escribiendo el validador @ISBN era parecida a esta. Por eso, si hubieras escrito tu código de prueba antes que el código de la aplicación, lo hubieras podido usar mientras que desarrollabas, lo cual es más eficiente que repetir una y otra vez a mano las pruebas con el navegador.
Fíjate que si usas @ISBN(search=false) esta prueba no funciona porque no solo comprueba el formato sino que también hace la búsqueda con el servicio REST. Por tanto, has de usar @ISBN sin atributos para anotar la propiedad isbn y poder ejecutar esta prueba.
Ahora ejecuta todas las prueba de tu aplicación Facturación para verificar que todo sigue en su sitio.
Resumen
En esta lección has aprendido varias formas de hacer validación en una aplicación OpenXava. Además, ahora estás preparado para encapsular toda la lógica de validación reutilizable en anotaciones usando Bean Validation.
La validación es una parte importante de la lógica de tu aplicación, y te ánimo a que la pongas en el modelo, es decir en las entidades; tal y como esta lección ha mostrado. Aun así, a veces es conveniente poner algo de lógica fuera de las clases del modelo. Aprenderás a hacer esto en las siguientes lecciones.
Table of Contents
Lección 6: Validación avanzada
De momento solo hemos hecho validaciones básicas usando la anotación @Required de OpenXava. A veces es necesario escribir nuestra propia lógica de validación. En el apéndice C se explica como funciona la validación. Aquí vamos a añadir validaciones con lógica propia a tu aplicación.Alternativas de validación
Vamos a refinar tu código para que el usuario no pueda asignar pedidos a una factura si los pedidos no han sido entregados todavía. Es decir, solo los pedidos entregados pueden asociarse a una factura. Aprovecharemos la oportunidad para explorar diferentes formas de hacer esta validación.Añadir la propiedad entregado a Pedido
Para hacer esto, lo primero es añadir una nueva propiedad a la entidad Pedido. La propiedad entregado:Además es necesario añadir la propiedad entregado a la vista. Modifica la vista Pedido como muestra el siguiente código:
Ahora tienes una nueva propiedad entregado que el usuario puede marcar para indicar que el pedido ha sido entregado. Ejecuta el nuevo código y marca algunos de los pedidos existentes como entregados.
Al arrancar la aplicación se añadirá la columna entregado a la tabla DocumentoComercial, ésta tendrá el valor null para todos los registros. Debemos actualizar esta columna con las siguiente sentencia SQL:
Validar con @EntityValidator
En tu aplicación actual el usuario puede añadir cualquier pedido que le plazca a una factura usando el módulo Factura, y puede asignar una factura a cualquier pedido desde el módulo Pedido. Vamos a restringir esto. Solo los pedidos entregados podrán añadirse a una factura.La primera alternativa que usaremos para implementar esta validación es mediante @EntityValidator. Esta anotación te permite asignar a tu entidad una clase con la lógica de validación deseada. Anotemos tu entidad Pedido tal como muestra el siguiente código:
Cada vez que un objeto Pedido se crea o modifica un objeto del tipo ValidadorEntregadoParaEstarEnFactura es creado, entonces las propiedades anyo, numero, factura y entregado se rellenan con las propiedades del mismo nombre del objeto Pedido. Después de eso, el método validate() del validador se ejecuta. Puedes ver el código del validador:
La lógica de validación es extremadamente fácil, si una factura está presente y este pedido no ha sido servido añadimos un mensaje de error, por tanto la validación fallará. Has de añadir el mensaje de error en el archivo Facturacion/i18n/MensajesFacturacion_es.properties. Tal como muestra a continuación:
Ahora puedes intentar añadir pedidos a una factura con la aplicación, verás como los pedidos no servidos son rechazados. Como se ve en la siguiente figura:
Ya tienes tu validación hecha con @EntityValidator. No es difícil, pero es un poco verboso, porque necesitas escribir una clase nueva solo para añadir 2 línea de lógica. Aprendamos otras formas de hacer esta misma validación.
Validar con métodos de retrollamada JPA
Vamos a probar otra forma más sencilla de hacer esta validación, simplemente moviendo la lógica de validación desde la clase validador a la misma entidad Pedido, en este caso a un método @PreUpdate.Lo primero es eliminar la clase ValidadorEntregadoParaEstarEnFactura de tu proyecto. También quita la anotación @EntityValidator de tu entidad Pedido:
Acabamos del eliminar la validación. Ahora, vamos a añadirla de nuevo, pero ahora dentro de la misma clase Pedido. Escribe el método validar() que se muestra a continuación dentro de tu clase Pedido:
Antes de grabar un pedido esta validación se ejecutará, si falla una ValidationException será lanzada. Esta excepción es del marco de validación Bean Validation, de esta forma OpenXava sabe que es una excepción de validación. Así con solo un método dentro de tu entidad tienes la validación hecha.
Validar en el setter
Otra alternativa para hacer tu validación es poner tu lógica de validación dentro del método setter. Es un enfoque simple y llano. Para probarlo, quita el método validar() de tu entidad Pedido y modifica el método setFactura() de la forma que mostramos a continuación:Esto funciona exactamente como las dos opciones anteriores. Es parecida a la alternativa del @PreUpdate, solo que no depende de JPA, es una implementación básica de Java.
Validar con Bean Validation
Como opción final vamos a hacer la más breve. Consiste en poner tu lógica de validación dentro de un método booleano anotado con la anotación de Bean Validation @AssertTrue.Para implementar esta alternativa primero quita la lógica de validación del método setFactura(). Después, añade isEntregadoParaEstarEnFactura() a tu entidad Pedido, como se muestra a continuación:
En las formas anteriores de validación nuestro mensaje de error era construído mediante dos argumentos, anyo y numero, que en nuestro archivo i18n son representados por {0}/{1} respectivamente. Para el caso de validación con @AssertTrue no podemos pasar estos dos argumentos para construir nuestro mensaje de error, sino que podemos declarar propiedades y propiedades calificadas del bean validado en la definición del mensaje. Veamos como modificar la definición del mensaje:
OpenXava llenará {anyo}/{numero} con los valores de anyo y numero que tenga el Pedido que está siendo actualizado y no cumple la condición de validación.
Esta es la forma más simple de validar, porque solo anotamos el método con la validación, y es el entorno Bean Validation el responsable de llamar este método al grabar, y lanzar la excepción correspondiente si la validación no pasa.
Validar al borrar con @RemoveValidator
Las validaciones que hemos visto hasta ahora se hacen cuando la entidad se modifica, pero a veces es útil hacer la validación justo al borrar la entidad, y usar la validación para vetar el borrado de la misma.Vamos a modificar la aplicación para impedir que un usuario borre un pedido si éste tiene una factura asociada. Para hacer esto anota tu entidad Pedido con @RemoveValidator, como se muestra a continuación:
Ahora, antes de borrar un pedido la lógica de ValidadorBorrarPedido se ejecuta, y si la validación falla el pedido no se borra. Veamos el código de este validador:
La lógica de validación está en el método validate(). Antes de llamarlo la entidad a validar es inyectada usando setEntity(). Si se añaden mensajes al objeto errors la validación fallará y la entidad no se borrará. Has de añadir el mensaje de error en el archivo Facturacion/i18n/MensajesFacturacion_es.properties:
Ahora si intentas borrar un pedido con una factura asociada obtendrás un mensaje de error y el borrado no se producirá.
Puedes ver que usar un @RemoveValidator no es difícil, pero es un poco verboso. Has de escribir una clase nueva solo para añadir un simple if. Examinemos una alternativa más breve:
Validar al borrar con un método de retrollamada
Vamos a probar otra forma más simple de hacer esta validación al borrar, moviendo la lógica de validación desde la clase validador a la misma entidad Pedido, en este caso en un método @PreRemove.El primer paso es eliminar la clase ValidadorBorrarPedido de tu proyecto. Además quita la anotación @RemoveValidator de tu entidad Pedido:
Hemos quitado la validación. Añadámosla otra vez, pero ahora dentro de la misma clase Pedido. Añade el método validarPreBorrar() a la clase Pedido, como se muestra a continuación:
Antes de borrar un pedido esta validación se efectuará, si falla se lanzará una ValidationException. Puedes lanzar cualquier excepción runtime para abortar el borrado. Tan solo con un método dentro de la entidad tienes la validación hecha.
¿Cuál es la mejor forma de validar?
Has aprendido varias formas de hacer la validación sobre tus clases del modelo. ¿Cuál de ellas es la mejor? Todas ellas son opciones válidas. Depende de tus circunstancias y preferencias personales. Si tienes una validación que no es trivial y es reutilizable en varios puntos de tu aplicación, entonces usar un @EntityValidator y @RemoveValidator es una buena opción. Por otra parte, si quieres usar tu modelo fuera de OpenXava y sin JPA, entonces el uso de la validación en los setters es mejor.En nuestro caso particular hemos optado por @AssertTrue para la validación “el pedido ha de estar servido para estar en una factura” y por @PreRemove para la validación al borrar. Ya que son las alternativas más simples que funcionan.
Crear tu propia anotación de Bean Validation
Las técnicas mencionadas hasta ahora son muy útiles para la mayoría de las validaciones de tus aplicaciones. Sin embargo, a veces te encuentras con algunas validaciones que son muy genéricas y quieres usarlas una y otra vez. En este caso definir tu propia anotación de Bean Validation puede ser una buena opción. Definir un Bean validation es más largo y engorroso que lo que hemos visto hasta ahora, pero usarlo y reusarlo es simple, tan solo añadir una anotación a tu propiedad o clase.Vamos a aprender como crear un Bean Validation.
Usar un Bean Validation en tu entidad
Es superfácil. Simplemente anota tu propiedad como ves a a continuación:Solo con añadir @ISBN a tu propiedad, y ésta será validada justo antes de que la entidad se grabe en la base de datos, ¡genial! El problema es que @ISBN no está incluida como un validador predefinido en el marco de validación Bean Validation. Esto no es un gran problema, si quieres una anotación @ISBN, hazla tú mismo. De hecho, vamos a crear la anotación de validación @ISBN en esta sección.
Antes de nada, añadamos una nueva propiedad isbn a Producto. Edita tu clase Producto y añádele el siguiente código:
Ejecuta el módulo Producto con tu navegador. Sí, la propiedad isbn ya está ahí. Ahora, puedes añadir la validación.
Definir tu propia anotación ISBN
Creemos la anotación @ISBN. Primero, crea un paquete en tu proyecto llamado org.openxava.facturacion.anotaciones, entonces sigue las instrucciones de la siguiente figura para crear una nueva anotación llamada ISBN.Edita el código de tu recién creada anotación ISBN y déjala así:
Como puedes ver, es una definición de anotación normal y corriente, solo que usas @Constraint para indicar la clase con la lógica de validación. Escribamos la clase ValidadorISBN.
Usa Apache Commons Validator para implementar la lógica
Vamos a escribir la clase ValidadorISBN con la lógica de validación para un ISBN. En lugar de escribir nosotros mismos la lógica para validar un ISBN usaremos el proyecto Commons Validator de Apache. Commons Validator contiene algoritmos de validación para direcciones de correo electrónico, fechas, URL y así por el estilo. El commons-validator.jar se incluye por defecto en los proyectos OpenXava, por tanto lo puedes usar sin ninguna configuración adicional.El código para ValidadorISBN lo puedes ver a continuación:
Como ves, la clase validador tiene que implementar ConstraintValidator del paquete javax.validation. Esto fuerza a tu validador a implementar initialize() e isValid(). El método isValid() contiene la lógica de validación. Fíjate que si el elemento a validar está vacío asumimos que es válido, porque validar si un valor está presente es responsabilidad de otras anotaciones, como @Required, y no de @ISBN.
En este caso la lógica de validación es sencillísima, porque nos limitamos a llamar al validador ISBN de Apache Commons Validator.
@ISBN está listo para usar. Para hacerlo anota tu propiedad isbn con él. Puedes ver cómo:
Ahora, puedes probar tu módulo, y verificar que el ISBN que introduces se valida correctamente. Enhorabuena, has escrito tu primer Bean Validation. No ha sido tan difícil: una anotación, una clase.
Este @ISBN es suficientemente bueno para usarlo en la vida real, sin embargo, vamos a mejorarlo un poco más, y así tendremos la posibilidad de experimentar con algunas posibilidades interesantes.
Llamar a un servicio web REST para validar el ISBN
Aunque la mayoría de los validadores tienen una lógica simple, puedes crear validadores con una lógica compleja si lo necesitas. Por ejemplo, en el caso de nuestro ISBN, queremos, no solo verificar el formato correcto, sino también comprobar que existe de verdad un libro con ese ISBN. Una forma de hacer esto es usando servicios web.Como seguramente ya sepas, un servicio web es una funcionalidad que reside en un servidor web y que tú puedes llamar desde tu programa. La forma tradicional de desarrollar servicios web es mediante los estándares WS-*, como SOAP, UDDI, etc. Aunque, hoy en día, la forma más simple de desarrollar servicios es REST. REST consiste básicamente en usar la ya existente “forma de trabajar” de internet para comunicación entre programas. Llamar a un servicio REST consiste en usar una URL web convencional para obtener un recurso de un servidor web. Este recurso usualmente contiene datos en formato XML, HTML, JSON, etc. En otras palabras, los programas usan internet de la misma manera que lo hacen los usuarios con sus navegadores.
Hay bastantes sitio con servicios web SOAP y REST para consultar el ISBN de un libro, pero no suele ser gratis. Por tanto, vamos a usar una alternativa más barata, que va a ser llamar a un sitio web convencional para hacer la búsqueda del ISBN, y examinar después la página resultado para determinar si la búsqueda ha funcionado. Algo así como un servicio web pseudo-REST.
Para llamar a la página web usaremos el marco de trabajo HtmlUnit. Aunque el principal cometido de este marco de trabajo sea crear pruebas para tus aplicaciones web, puedes usarlo para leer cualquier página web. Lo usaremos porque es más fácil que otras librerías con este propósito, como por ejemplo Apache Commons HttpClient. Observa lo simple que es leer una página web con HtmlUnit:
Después de esto, puedes usar el objeto pagina para manipular la página leída.
OpenXava usa HtmlUnit como marco subyacente para las pruebas, por tanto ya está incluido en OpenXava, pero no se incluye por defecto en las aplicaciones OpenXava, así que tienes que incluirlo tú mismo en tu aplicación. Para hacerlo copia los archivos cssparser.jar, htmlunit-core-js.jar, htmlunit.jar, httpclient.jar, httpcore.jar, httpmime.jar, nekothml.jar, sac.jar, xalan.jar, xercesImpl.jar, xml-apis.jar, desde la carpeta Openxava/lib a la carpeta Facturacion/web/WEB-INF/lib.
Modifiquemos ValidadorISBN para que haga uso de este servicio REST. Puedes ver el resultado:
Simplemente buscamos una página usando como argumento en la URL el ISBN. Si la página resultante no contiene el mensaje de error "can't be found" la búsqueda ha sido satisfactoria. El método pagina.asText() devuelve el contenido de la página HTML sin las marcas HTML, es decir, con solo la información textual.
Puedes usar este truco con cualquier sitio que te permita hacer búsquedas, así puedes consultar virtualmente millones de sitios web desde tu aplicación. En un servicio REST más puro el resultado hubiera sido un documento JSON o XML en vez de uno HTML, pero hubieras tenido que pasar por caja.
Prueba ahora tu aplicación y verás como si introduces un ISBN no existente la validación falla.
Añadir atributos a tu anotación
Creas una anotación Bean Validation cuando quieres reutilizar la validación varias veces, usualmente en varios proyectos. En este caso, necesitas hacer tu validación adaptable, para que sea reutilizable de verdad. Por ejemplo, en el proyecto actual buscar en www.bookfinder4u.com el ISBN es conveniente, pero en otro proyecto, o incluso en otra entidad de tu actual proyecto, puede que no quieras hacer esa búsqueda. Necesitas hacer tu anotación más flexible.La forma de añadir esta flexibilidad a tu anotación de validación es mediante los atributos. Por ejemplo, podemos añadir un atributo de búsqueda booleano a nuestra anotación ISBN para poder escoger si queremos buscar el ISBN en internet para validar o no. Para hacerlo, simplemente añade el atributo buscar al código de la anotación ISBN, tal como muestra el siguiente código:
Este nuevo atributo buscar puede leerse de la clase validador. Míra como:
Aquí ves la utilidad del método initialize(), que lee la anotación para inicializar el validador. En este caso simplemente almacenamos el valor de isbn.buscar() para preguntar por él en isValid().
Ahora puedes escoger si quieres llamar a nuestro servicio pseudo-REST o no para hacer la validación ISBN. Mira como:
¡Enhorabuena! Has aprendido como crear tu propia anotación de Bean Validation, y de paso a usar la útil herramienta HtmlUnit.
Pruebas JUnit
Nuestra meta no es desarrollar una ingente cantidad de código, sino crear software de calidad. Al final, si creas software de calidad acabarás escribiendo más cantidad de software, porque podrás dedicar más tiempo a hacer cosas nuevas y excitantes, y menos depurando legiones de bugs. Y tú sabes que la única forma de conseguir calidad es mediante las pruebas automáticas, por tanto actualicemos nuestro código de prueba.Probar la validación al añadir a una colección
Recuerda que hemos refinado tu código para que el usuario no pueda asignar pedidos a una factura si los pedidos no están servidos. Después de esto, tu actual testAnyadirPedidos() de PruebaFactura puede fallar, porque trata de añadir el primer pedido, y es posible que ese primer pedido no esté marcado como servido.Modifiquemos la prueba para que funcione y también para comprobar la nueva funcionalidad de validación. Mira como:
Hemos modificado la parte de la selección de pedidos a añadir, antes seleccionábamos el primero, no importaba si estaba servido o no. Ahora seleccionamos un pedido servido y otro no servido, de esta forma comprobamos que el pedido servido se añade y el no servido es rechazado.
La pieza que nos falta es la forma de seleccionar los pedidos. Esto es el trabajo del método seleccionarPrimerPedidoConEntregadoIgual(). Veámoslo:
Aquí ves una buena técnica para hacer un bucle sobre los elementos visualizados de una lista para seleccionarlos y coger algunos datos, o cualquier otra cosa que quieras hacer con los datos de la lista.
Probar validación al asignar una referencia y al borrar
Desde el módulo Factura el usuario no pueda asignar pedidos a una factura si los pedidos no están servidos, por lo tanto, desde el módulo Pedido el usuario tampoco debe poder asignar una factura a un pedido si éste no está servido. Es decir, hemos de probar también la otra parte de la asociación. Lo haremos modificando el actual testPonerFactura() de PruebaPedido.Además, aprovecharemos este caso para probar la validación al borrar que vimos en las secciones Validar al borrar con @RemoveValidator y Validar al borrar con un método de retrollamada. Allí modificamos la aplicación para impedir que un usuario borrara un pedido si éste tenía una factura asociada. Ahora probaremos este hecho.
Todo esto está en el testPonerFactura() mejorado que puedes ver a continuación:
La prueba original solo buscaba una factura, ni siquiera intentaba grabarla. Ahora, hemos añadido código al final para probar la grabación de un pedido marcado como servido, y marcado como no servido, de esta forma comprobamos la validación. Después de eso, tratamos de borrar el pedido, el cual tiene una factura, así probamos también la validación al borrar.
Probar el Bean Validation propio
Solo nos queda probar tu Bean Validation ISBN, el cual usa un servicio REST para hacer la validación. Simplemente hemos de escribir una prueba que trate de asignar un ISBN incorrecto, uno inexistente y uno correcto a un producto, y ver que pasa. Para hacer esto añadamos un método testValidarISBN() a PruebaProducto.Seguramente la prueba manual que hacías mientras estabas escribiendo el validador @ISBN era parecida a esta. Por eso, si hubieras escrito tu código de prueba antes que el código de la aplicación, lo hubieras podido usar mientras que desarrollabas, lo cual es más eficiente que repetir una y otra vez a mano las pruebas con el navegador.
Fíjate que si usas @ISBN(search=false) esta prueba no funciona porque no solo comprueba el formato sino que también hace la búsqueda con el servicio REST. Por tanto, has de usar @ISBN sin atributos para anotar la propiedad isbn y poder ejecutar esta prueba.
Ahora ejecuta todas las prueba de tu aplicación Facturación para verificar que todo sigue en su sitio.
Resumen
En esta lección has aprendido varias formas de hacer validación en una aplicación OpenXava. Además, ahora estás preparado para encapsular toda la lógica de validación reutilizable en anotaciones usando Bean Validation.La validación es una parte importante de la lógica de tu aplicación, y te ánimo a que la pongas en el modelo, es decir en las entidades; tal y como esta lección ha mostrado. Aun así, a veces es conveniente poner algo de lógica fuera de las clases del modelo. Aprenderás a hacer esto en las siguientes lecciones.
Descargar código fuente de esta lección
¿Problemas con la lección? Pregunta en el foro ¿Ha ido bien? Ve a la lección 7